#ifndef HSYSTEMINFO_H
#define HSYSTEMINFO_H

#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QString>

#ifdef QT_DEBUG
//#define USE_SIGAR
#endif
#ifdef USE_SIGAR
    #include "sigar-bin/include/sigar.h"
    extern "C"
    {
        #include "sigar-bin/include/sigar_format.h"
    }
#endif

#ifdef Q_OS_WIN32
    #include <windows.h>
    #include <psapi.h>
    #pragma comment(lib,"psapi.lib")
#endif

//for unit conversion to/from MB/GB
const int KB = 1024;
const int MB = KB * 1024;
const int GB = MB * 1024;
const double KiB = 1024.;
const double MiB = KiB * 1024;
const double GiB = MiB * 1024;

#ifdef Q_OS_WIN32
static quint64 file_time_2_utc(const FILETIME* ftime)
{
    LARGE_INTEGER li;

    //assert(ftime);
    li.LowPart = ftime->dwLowDateTime;
    li.HighPart = ftime->dwHighDateTime;
    return li.QuadPart;
}

static int get_processor_number()
{
    SYSTEM_INFO info;
    GetSystemInfo(&info);
    return (int)info.dwNumberOfProcessors;
}

static int get_cpu_usage()
{
    static int processor_count_ = -1;

    static qint64 last_time_ = 0;
    static qint64 last_system_time_ = 0;

    FILETIME now;
    FILETIME creation_time;
    FILETIME exit_time;
    FILETIME kernel_time;
    FILETIME user_time;

    qint64 system_time;
    qint64 time;
    qint64 system_time_delta;
    qint64 time_delta;

    int cpu = -1;

    if(processor_count_ == -1)
    {
        processor_count_ = get_processor_number();
    }

    GetSystemTimeAsFileTime(&now);

    if (!GetProcessTimes(GetCurrentProcess(), &creation_time, &exit_time,
        &kernel_time, &user_time))
    {
        // We don't assert here because in some cases (such as in the Task Manager)
        // we may call this function on a process that has just exited but we have
        // not yet received the notification.
        return -1;
    }
    system_time = ( file_time_2_utc(&kernel_time) + file_time_2_utc(&user_time) ) / processor_count_;
    time = file_time_2_utc(&now);

    if ((last_system_time_ == 0) || (last_time_ == 0))
    {
        // First call, just set the last values.
        last_system_time_ = system_time;
        last_time_ = time;
        return -1;
    }

    system_time_delta = system_time - last_system_time_;
    time_delta = time - last_time_;

    //assert(time_delta != 0);

    if (time_delta == 0)
        return -1;

    // We add time_delta / 2 so the result is rounded.
    cpu = (int)((system_time_delta * 100 + time_delta / 2) / time_delta);
    last_system_time_ = system_time;
    last_time_ = time;

    return cpu;
}

static int get_memory_usage(PPROCESS_MEMORY_COUNTERS ppsmemCounters)
{
    if ( ppsmemCounters )
    {
        if(GetProcessMemoryInfo(GetCurrentProcess(), ppsmemCounters, sizeof(PROCESS_MEMORY_COUNTERS)))
        {
            //quint64 mem, quint64 vmem;
            //mem  = pmc.WorkingSetSize;
            //vmem = pmc.PagefileUsage;
            return 0;
        }
    }
    return -1;
}

static int get_io_bytes(quint64* read_bytes, quint64* write_bytes)
{
    IO_COUNTERS io_counter;
    if(GetProcessIoCounters(GetCurrentProcess(), &io_counter))
    {
        if(read_bytes) *read_bytes   = io_counter.ReadTransferCount;
        if(write_bytes) *write_bytes = io_counter.WriteTransferCount;
        return 0;
    }
    return -1;
}
#endif

static void showSystemInfoByApi(QString title)
{
    if (title.isEmpty())
    {
        title = "unknown class";
    }

    static QDateTime last_show_time = QDateTime::currentDateTime();
    QDateTime time = QDateTime::currentDateTime();
    qint64 increaseTime = last_show_time.msecsTo( time );
    last_show_time = time;

#ifdef Q_OS_WIN32
    static PROCESS_MEMORY_COUNTERS last_pmc;
    int cpu = get_cpu_usage();
    if (cpu < 0)
    {
        cpu = 0;
    }
    else if (cpu>100)
    {
        cpu = 100;
    }

    PROCESS_MEMORY_COUNTERS pmc;
    get_memory_usage(&pmc);
    int increaseMemSize = pmc.WorkingSetSize- last_pmc.WorkingSetSize;
    last_pmc = pmc;

    qDebug( "[%s] <%s> ---\tcpu usage %d%%;\tmemory size %d K;\tincrease size %d K;\ttime interval %lld ms.",
            time.toString("yyyy-MM-dd hh:mm:ss zzz").toLatin1().data(),
            title.toLatin1().data(),
            cpu,
            pmc.WorkingSetSize/KB,
            increaseMemSize/KB,
            increaseTime );
#else
    qDebug( "[%s] <%s>\t --- \ttime interval %d ms.",
            time.toString("yyyy-MM-dd hh:mm:ss zzz").toLatin1().data(),
            title.toLatin1().data(),
            increaseTime );
#endif
}

#ifdef USE_SIGAR
static void showSystemInfoBySigar(QString title)
{
    if (title.isEmpty())
    {
        title = "unknown class";
    }

    static QDateTime last_show_time = QDateTime::currentDateTime();
    QDateTime time = QDateTime::currentDateTime();
    qint64 increaseTime = last_show_time.msecsTo( time );
    last_show_time = time;

    //init sigar
    sigar_t *sigar;
    sigar_open(&sigar);
    sigar_cpu_info_list_t cpulist;
    sigar_cpu_info_list_get(sigar, &cpulist);
    sigar_pid_t pid = sigar_pid_get(sigar);

    //get cpu usage info
    sigar_proc_cpu_t cpu;
    sigar_proc_cpu_get(sigar, pid, &cpu);

    //get memory info
    static sigar_proc_mem_t last_mem;
    sigar_proc_mem_t        mem;
    sigar_proc_mem_get(sigar, pid, &mem);
    quint64 memSize = mem.resident;
    qint64 increaseMemSize = mem.resident - last_mem.resident;
    last_mem = mem;

    //calculate the cpu usage percent
    float percent = 0.0;
    int status = sigar_proc_cpu_get(sigar, pid, &cpu);
    if (status == SIGAR_OK)
    {
        percent = cpu.percent * 100.0 / cpulist.size;
        if (percent < 0)
        {
            percent = 0;
        }
        else if (percent>100)
        {
            percent = 100;
        }
    }

    //output debug info
    qDebug( "[%s] <%s> ---\tcpu usage %.2lf%%;\tmemory size %llu K;\tincrease size %lld K;\ttime interval %lld ms.",
            time.toString("yyyy-MM-dd hh:mm:ss zzz").toLatin1().data(),
            title.toLatin1().data(),
            percent,
            memSize/KB,
            increaseMemSize/KB,
            increaseTime );

    //release sigar
    sigar_close(sigar);
}
#endif

static void showSystemInfo(QString title)
{
    //QDateTime time_begin = QDateTime::currentDateTime();
#ifdef USE_SIGAR
    showSystemInfoBySigar(title);
#else
    showSystemInfoByApi(title);
#endif
    //QDateTime time_end = QDateTime::currentDateTime();
    //qint64 increaseTime = time_begin.msecsTo( time_end );
    //qDebug( "\r\n[%s] --- \tThe time interval of the showSystemInfo() initialization process : %lld ms ---> %lld s.\r\n", time_end.toString("yyyy-MM-dd hh:mm:ss zzz").toLatin1().data(), increaseTime, increaseTime/1000 );

}

#endif // HSYSTEMINFO_H
